The goal of this project is to investigate how partnerships involving multiple top-tier players in the NBA impacts various performance measures and team outcomes. Among the research questions we would like to explore are the following:

[ INSERT RESEARCH QUESTIONS HERE ]

To be able to investigate, we need to pull data from multiple NBA seasons. The script below provides code to create functions that pull traditional stats for every player for a given user-defined season.

Load necessary libraries

<<<<<<< Updated upstream
library(rvest)
library(dplyr)
library(tidyverse)
── Attaching core tidyverse packages ─────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ forcats   1.0.0     ✔ readr     2.1.5
✔ ggplot2   3.5.1     ✔ stringr   1.5.1
✔ lubridate 1.9.3     ✔ tibble    3.2.1
✔ purrr     1.0.2     ✔ tidyr     1.3.1
── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter()         masks stats::filter()
✖ readr::guess_encoding() masks rvest::guess_encoding()
✖ dplyr::lag()            masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
=======
library(rvest)
library(dplyr)
library(tidyverse)
library(httr)
>>>>>>> Stashed changes

Function to get NBA roster for a specified year

get_nba_roster <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)


  # Extract the table containing the player statistics
  roster_table <- webpage %>%
    html_node("table#per_game_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
  roster_table <- roster_table %>%
    filter(Player != "Player")
    return(roster_table)
}

Example usage

year <- 2018  # Specify the year
nba_roster <- get_nba_roster(year)

#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)
NA
#Summary statistics

position_roster<-filter(nba_roster,Pos!="PG" )
position_roster

library(plotly)

# Assuming 'nba_roster' is your data frame
input <- nba_roster[, c('MP', 'PTS', 'Player','Pos')]

# Create the plotly scatter plot
fig <- plot_ly(input, x = ~MP, y = ~PTS, type = 'scatter', mode = 'markers',
               text = ~Player,  # This adds player names on hover
               hoverinfo = 'text', # Ensures that only player names appear on hover
               color = ~Pos,  # Colors points based on position
               marker = list(size = 10))

# Set the plot title and axis labels
fig <- fig %>% layout(title = "Minutes Played vs Points Scored",
                      xaxis = list(title = "Minutes Played", range = c(0, 48)),
                      yaxis = list(title = "Points", range = c(0, 35)))

# Show the plot
fig
Warning: Ignoring 1 observations
Warning: Ignoring 1 observations
#Data Visualization for Minutes Played vs Points Scored
input <- nba_roster[, c('MP', 'PTS')]
print(head(input))

# Get the input values.
input <- nba_roster[, c('MP', 'PTS')]

# Plot the chart for cars with
# weight between 1.5 to 4 and
# mileage between 10 and 25.
plot(x = input$MP, y = input$PTS,
    xlab = "Minutes Played",
    ylab = "Points",
    xlim = c(0.0, 48),
    ylim = c(0.0, 35),   
    main = "Minutes Played vs Points Scored"
)

#Data Visualization for Field Goals Attempled vs Field Goals Made
input_2 <- nba_roster[, c('FGA', 'FG')]
print(head(input_2))

# Get the input values.
input_2 <- nba_roster[, c('FGA', 'FG')]

# Plot the chart for players with
# field goal attempts between 0.0 to 25.0 and
b_FG<-max(input_2$FG,na.rm=T)
b_FGA<-max(input_2$FGA,na.rm=T)
# Field Goal Made between 0.0 and 25.0
plot(x = input_2$FGA, y = input_2$FG,
    xlab = "Field Goal Attempts",
    ylab = "Field Goal Made",
    xlim = c(0.0, b_FGA),
    ylim = c(0.0, b_FG),     
    main = "Field Goal Attempt vs Field Goal Made"
)

NA
NA
# Create the data for the chart
A <- c(nba_roster$PTS)
B <- c("PF", "PG", "SF", "C", "SG")

# Plot the bar chart
ggplot(nba_roster, aes(x=Pos, fill=PTS))+  

geom_bar()+  

theme_classic(16)+  

xlab("Position")+  

ylab("Points") 
Warning: The following aesthetics were dropped during statistical transformation: fill.
ℹ This can happen when ggplot fails to infer the correct grouping structure in the data.
ℹ Did you forget to specify a `group` aesthetic or to convert a numerical variable into a factor?

ASSIGNMENT 1: Is the data “clean”? Are there any missing values to be accounted for/addressed? If there are any data quality issues,

Initial thought to change the default character to double given that we have fractioned values. i think the columns should be changed from ” chararcter” to “double”

For players who are missing data

nba_roster<-na.omit(nba_roster)

nba_roster




# Convert specific columns from character to double
# Convert all character columns to double
nba_roster %>%
   mutate(across(G:PTS, as.numeric))
NA
NA
NA

To determine whether a player is “top tier” and should be considered a part of a “Big 3” lineup, other authors have transformed traditional stats to create metrics such as

PRA = POINTS + REBOUNDS + ASSISTS

We will consider advanced statistics such as PLAYER EFFIFIENCY RATING:

PER = (PTS + REB + AST + STL + BLK − Missed FG − Missed FT - TO) /GP

In particular, Value over Replacement (VORP) seems to do a solid job of identifying the best players in the league.

The script below provide code to create functions that pull advanced stats for every player for a given user-defined season.


# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)
  
  # Extract the table containing the advanced player statistics
  advanced_stats_table <- webpage %>%
    html_node("table#advanced_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
 # advanced_stats_table <- advanced_stats_table %>%
  #  filter(Player != "Player")
  
  return(advanced_stats_table)
}

# Example usage
year <- 2018  # Specify the year
nba_advanced_stats <- get_nba_advanced_stats(year)

# Print the first few rows of the advanced stats
head(nba_advanced_stats)
NA
NA

ASSIGNMENT 2: Is the advanced data “clean”? Are there any missing values to be accounted for/addressed? If there are any data quality issues,

cleaning similar to first one

The script below provide code to clean out the quality issues presented in the dataframe

#1 We want to order the athletes name to alphabetical order to clean out the filler headers present

newdataframe<- dataframe[order(dataframe$Player)]
Error: object 'dataframe' not found

#want to order by alphabetic name to make cleaning out the filler headers from the dataset
AO_nba_advanced_stats <- nba_advanced_stats[order(nba_advanced_stats$Player),]



# remove filler rows that had been previously used as headers on webpage
AO_nba_advanced_stats<- AO_nba_advanced_stats[-c(502:526), ]


#remove na from dataframe
AO_nba_advanced_stats %>% 
  select(where(~!all(is.na(.))))
#removing column 20 and 25 from dataframe since theyre blanks
AO_nba_advanced_stats<-AO_nba_advanced_stats[,-20]
AO_nba_advanced_stats<-AO_nba_advanced_stats[,-24]


#change range of cloumns <dbl> from <chr>

AO_nba_advanced_stats %>%
   mutate(across(G:VORP, as.numeric))
Warning: There were 22 warnings in `mutate()`.
The first warning was:
ℹ In argument: `across(G:VORP, as.numeric)`.
Caused by warning:
! NAs introduced by coercion
ℹ Run ]8;;ide:run:dplyr::last_dplyr_warnings()dplyr::last_dplyr_warnings()]8;; to see the 21 remaining warnings.

ASSIGNMENT 3: Merge the cleaned up datasets to create one new data frame with the traditional and advanced stats.

<<<<<<< Updated upstream
#how to merge two files into one new data frame
nba_merge<-merge(nba_roster, AO_nba_advanced_stats, by = c("Rk", "Player", "Pos","Age", "Tm","G"))#, by.y = c("Rk", "Player", "Pos","Age", "Tm","G") , all.x = TRUE, all.y = TRUE)
=======

#Get NBA Totals Statistics
get_nba_totals_stats <- function(year) {
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_totals.html")
  webpage <- read_html(url)
  totals_stats_table <- webpage %>%
    html_node("table#totals_stats") %>%
    html_table(fill = TRUE)

  # Clean up column names
  colnames(totals_stats_table) <- make.names(colnames(totals_stats_table), unique = TRUE)

  # Clean the data
  totals_stats_table <- totals_stats_table %>%
    filter(!is.na(Player) & Player != "Player")  # Ensure no NA or duplicate header rows
  
  return(totals_stats_table)
}


get_nba_advanced_stats <- function(year) {
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
  
  # Fetch webpage
  webpage <- tryCatch({
    read_html(GET(url, user_agent("Mozilla/5.0")))
  }, error = function(e) {
    stop("Error fetching webpage: ", e$message)
  })
  
  # Extract table with updated ID
  advanced_stats_table <- webpage %>%
    html_node("table#advanced") %>%  # Updated selector to match the new ID
    html_table(fill = TRUE)
  
  # Clean up column names
  colnames(advanced_stats_table) <- make.names(colnames(advanced_stats_table), unique = TRUE)
  
  # Clean the data
  advanced_stats_table <- advanced_stats_table %>%
    filter(!is.na(Player) & Player != "Player")  # Remove NA rows and duplicate headers
  
  return(advanced_stats_table)
}
>>>>>>> Stashed changes
Error in fix.by(by.x, x) : 'by' must specify a uniquely valid column

ASSIGNMENT 4: Make a function with argument year that outputs one dataframe with the merged traditional and advanced data.

<<<<<<< Updated upstream

combined_nba_stats<-function(year){
get_nba_roster2 <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_per_game.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)

=======


#Get NBA Totals Statistics
get_nba_totals_stats <- function(year) {
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_totals.html")
  webpage <- read_html(url)
  totals_stats_table <- webpage %>%
    html_node("table#totals_stats") %>%
    html_table(fill = TRUE)

  # Clean up column names
  colnames(totals_stats_table) <- make.names(colnames(totals_stats_table), unique = TRUE)

  # Clean the data
  totals_stats_table <- totals_stats_table %>%
    filter(!is.na(Player) & Player != "Player")  # Ensure no NA or duplicate header rows
  
  return(totals_stats_table)
}


get_nba_advanced_stats <- function(year) {
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
  
  # Fetch webpage
  webpage <- tryCatch({
    read_html(GET(url, user_agent("Mozilla/5.0")))
  }, error = function(e) {
    stop("Error fetching webpage: ", e$message)
  })
  
  # Extract table with updated ID
  advanced_stats_table <- webpage %>%
    html_node("table#advanced") %>%  # Updated selector to match the new ID
    html_table(fill = TRUE)
  
  # Clean up column names
  colnames(advanced_stats_table) <- make.names(colnames(advanced_stats_table), unique = TRUE)
  
  # Clean the data
  advanced_stats_table <- advanced_stats_table %>%
    filter(!is.na(Player) & Player != "Player")  # Remove NA rows and duplicate headers
  
  return(advanced_stats_table)
}

get_cleaned_nba_stats <- function(year) {
  # Fetch totals and advanced stats
  nba_totals <- get_nba_totals_stats(year)
  nba_advanced <- get_nba_advanced_stats(year)

  # Print to check datasets (Optional)
  print("NBA Totals:")
  print(head(nba_totals))
  print("NBA Advanced:")
  print(head(nba_advanced))
>>>>>>> Stashed changes

  # Extract the table containing the player statistics
  roster_table <- webpage %>%
    html_node("table#per_game_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
  roster_table <- roster_table %>%
    filter(Player != "Player")
    return(roster_table)
}
  
  year <- 2023  # Specify the year
nba_roster2 <- get_nba_roster2(year)

#Print the first few rows of the roster
head(nba_roster)
tail(nba_roster)


<<<<<<< Updated upstream
#take out the N/A 
nba_roster2<-na.omit(nba_roster2)
=======
  # Debug: Show merged data sample
  print("Merged Data Sample:")
  print(head(nba_merge))
  
  # Check column names to confirm 'Team' exists
  print("Column Names Before Renaming:")
  print(colnames(nba_merge))

  # Clean 'Team' column: rename 'Tm' to 'Team' if present
  if ("Tm" %in% colnames(nba_merge)) {
    nba_merge <- nba_merge %>%
      rename(Team = Tm)
  }

  # Debug: Show column names after renaming
  print("Column Names After Renaming 'Tm' to 'Team':")
  print(colnames(nba_merge))

  # Handle duplicate columns like Team.x, Team.y, Awards.x, Awards.y
  duplicate_columns <- colnames(nba_merge)[grepl("\\.x$", colnames(nba_merge))]

  for (col in duplicate_columns) {
    # Extract the base name of the column (e.g., "Team" from "Team.x")
    base_col <- sub("\\.x$", "", col)
    
    # Merge the .x and .y columns into one
    if (paste0(base_col, ".y") %in% colnames(nba_merge)) {
      nba_merge <- nba_merge %>%
        mutate(!!base_col := coalesce(get(col), get(paste0(base_col, ".y")))) %>%
        select(-all_of(c(col, paste0(base_col, ".y"))))  # Drop the old columns
    }
  }
>>>>>>> Stashed changes


# Convert specific columns from character to double

<<<<<<< Updated upstream
nba_roster2 %>%
   mutate(across(G:PTS, as.numeric))

#ADVANCED STATS

# Function to get NBA advanced stats for a specified year
get_nba_advanced_stats <- function(year) {
  # Construct the URL for the specified year
  url <- paste0("https://www.basketball-reference.com/leagues/NBA_", year, "_advanced.html")
  
  # Read the HTML content from the URL
  webpage <- read_html(url)
  
  # Extract the table containing the advanced player statistics
  advanced_stats_table <- webpage %>%
    html_node("table#advanced_stats") %>%
    html_table(fill = TRUE)
  
  # Clean the data (remove header rows that might be duplicated)
 # advanced_stats_table <- advanced_stats_table %>%
  #  filter(Player != "Player")
  
  return(advanced_stats_table)
}

# Example usage
year <- 2023  # Specify the year
nba_advanced_stats2<- get_nba_advanced_stats(year)

# Print the first few rows of the advanced stats
head(nba_advanced_stats2)



#want to order by alphabetic name to make cleaning out the filler headers from the dataset
AO_nba_advanced_stats2<- nba_advanced_stats2[order(nba_advanced_stats2$Player),]





#remove na from dataframe
AO_nba_advanced_stats2 %>% 
  select(where(~!all(is.na(.))))
#removing column 20 and 25 from dataframe since theyre blanks
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-20]
AO_nba_advanced_stats2<-AO_nba_advanced_stats2[,-24]


# remove filler rows that had been previously used as headers on webpage


AO_nba_advanced_stats2 <- AO_nba_advanced_stats2[AO_nba_advanced_stats2$Player != "Player",]

AO_nba_advanced_stats2$Player <- factor(AO_nba_advanced_stats2$Player)




#change range of cloumns <dbl> from <chr>

AO_nba_advanced_stats2 %>%
   mutate(across(G:VORP, as.numeric))
   
   nba_merge<-merge(nba_roster2, AO_nba_advanced_stats2, by.x = c("Rk", "Player", "Pos","Age", "Tm","G"), by.y = c("Rk", "Player", "Pos","Age", "Tm","G") , all.x = TRUE, all.y = TRUE)
   
}
======= # Remove the 'X', 'X.1' columns if they exist columns_to_remove <- c("X", "X.1") nba_merge <- nba_merge %>% select(-any_of(columns_to_remove)) # Remove specified columns if they exist # Merge 'Rk.x' and 'Rk.y' columns if ("Rk.x" %in% names(nba_merge) & "Rk.y" %in% names(nba_merge)) { nba_merge <- nba_merge %>% mutate(Rk = coalesce(as.character(Rk.x), as.character(Rk.y))) %>% select(-Rk.x, -Rk.y) } # Merge 'Age.x' and 'Age.y' columns if ("Age.x" %in% names(nba_merge) & "Age.y" %in% names(nba_merge)) { nba_merge <- nba_merge %>% mutate(Age = coalesce(as.numeric(Age.x), as.numeric(Age.y))) %>% select(-c(Age.x, Age.y)) } # Reorder columns for clarity column_order <- c("Player", "Pos", "Age", "Rk", "G", "MP", "Team") nba_merge <- nba_merge %>% select(all_of(column_order), everything()) # Return the cleaned and merged dataset return(nba_merge) } # Example usage nba_data_2013 <- get_cleaned_nba_stats(2013)
>>>>>>> Stashed changes
Error in value[[3L]](cond) : 
  Error fetching webpage: could not find function "GET"
<<<<<<< Updated upstream =======
nba_data_1984<-get_cleaned_nba_stats(1984)
Error in open.connection(x, "rb") : 
  Could not resolve host: www.basketball-reference.com
>>>>>>> Stashed changes

ASSIGNMENT 5: Make this file more visually appealng, with headers, bullet points, sections and subsections as you see fit. You may consider migrating over to Quarto for this reason.

<<<<<<< Updated upstream
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhlIGdvYWwgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGludmVzdGlnYXRlIGhvdyBwYXJ0bmVyc2hpcHMgaW52b2x2aW5nIG11bHRpcGxlIHRvcC10aWVyIHBsYXllcnMgaW4gdGhlIE5CQSBpbXBhY3RzIHZhcmlvdXMgcGVyZm9ybWFuY2UgbWVhc3VyZXMgYW5kIHRlYW0gb3V0Y29tZXMuIEFtb25nIHRoZSByZXNlYXJjaCBxdWVzdGlvbnMgd2Ugd291bGQgbGlrZSB0byBleHBsb3JlIGFyZSB0aGUgZm9sbG93aW5nOgoKKlsgSU5TRVJUIFJFU0VBUkNIIFFVRVNUSU9OUyBIRVJFIF0qCgpUbyBiZSBhYmxlIHRvIGludmVzdGlnYXRlLCB3ZSBuZWVkIHRvIHB1bGwgZGF0YSBmcm9tIG11bHRpcGxlIE5CQSBzZWFzb25zLiBUaGUgc2NyaXB0IGJlbG93IHByb3ZpZGVzIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgdHJhZGl0aW9uYWwgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgoKTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpGdW5jdGlvbiB0byBnZXQgTkJBIHJvc3RlciBmb3IgYSBzcGVjaWZpZWQgeWVhcgpgYGB7cn0KZ2V0X25iYV9yb3N0ZXIgPC0gZnVuY3Rpb24oeWVhcikgewogICMgQ29uc3RydWN0IHRoZSBVUkwgZm9yIHRoZSBzcGVjaWZpZWQgeWVhcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3Blcl9nYW1lLmh0bWwiKQogIAogICMgUmVhZCB0aGUgSFRNTCBjb250ZW50IGZyb20gdGhlIFVSTAogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKCgogICMgRXh0cmFjdCB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcGxheWVyIHN0YXRpc3RpY3MKICByb3N0ZXJfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjcGVyX2dhbWVfc3RhdHMiKSAlPiUKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YSAocmVtb3ZlIGhlYWRlciByb3dzIHRoYXQgbWlnaHQgYmUgZHVwbGljYXRlZCkKICByb3N0ZXJfdGFibGUgPC0gcm9zdGVyX3RhYmxlICU+JQogICAgZmlsdGVyKFBsYXllciAhPSAiUGxheWVyIikKICAgIHJldHVybihyb3N0ZXJfdGFibGUpCn0KYGBgCgpFeGFtcGxlIHVzYWdlCmBgYHtyfQp5ZWFyIDwtIDIwMTggICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfcm9zdGVyIDwtIGdldF9uYmFfcm9zdGVyKHllYXIpCgojUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSByb3N0ZXIKaGVhZChuYmFfcm9zdGVyKQp0YWlsKG5iYV9yb3N0ZXIpCgpgYGAKYGBge3J9CiNTdW1tYXJ5IHN0YXRpc3RpY3MKCnBvc2l0aW9uX3Jvc3RlcjwtZmlsdGVyKG5iYV9yb3N0ZXIsUG9zIT0iUEciICkKcG9zaXRpb25fcm9zdGVyCmBgYAoKCgpgYGB7cn0KCmxpYnJhcnkocGxvdGx5KQoKIyBBc3N1bWluZyAnbmJhX3Jvc3RlcicgaXMgeW91ciBkYXRhIGZyYW1lCmlucHV0IDwtIG5iYV9yb3N0ZXJbLCBjKCdNUCcsICdQVFMnLCAnUGxheWVyJywnUG9zJyldCgojIENyZWF0ZSB0aGUgcGxvdGx5IHNjYXR0ZXIgcGxvdApmaWcgPC0gcGxvdF9seShpbnB1dCwgeCA9IH5NUCwgeSA9IH5QVFMsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbWFya2VycycsCiAgICAgICAgICAgICAgIHRleHQgPSB+UGxheWVyLCAgIyBUaGlzIGFkZHMgcGxheWVyIG5hbWVzIG9uIGhvdmVyCiAgICAgICAgICAgICAgIGhvdmVyaW5mbyA9ICd0ZXh0JywgIyBFbnN1cmVzIHRoYXQgb25seSBwbGF5ZXIgbmFtZXMgYXBwZWFyIG9uIGhvdmVyCiAgICAgICAgICAgICAgIGNvbG9yID0gflBvcywgICMgQ29sb3JzIHBvaW50cyBiYXNlZCBvbiBwb3NpdGlvbgogICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCkpCgojIFNldCB0aGUgcGxvdCB0aXRsZSBhbmQgYXhpcyBsYWJlbHMKZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHRpdGxlID0gIk1pbnV0ZXMgUGxheWVkIHZzIFBvaW50cyBTY29yZWQiLAogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIk1pbnV0ZXMgUGxheWVkIiwgcmFuZ2UgPSBjKDAsIDQ4KSksCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiUG9pbnRzIiwgcmFuZ2UgPSBjKDAsIDM1KSkpCgojIFNob3cgdGhlIHBsb3QKZmlnCgoKYGBgCmBgYHtyfQojRGF0YSBWaXN1YWxpemF0aW9uIGZvciBNaW51dGVzIFBsYXllZCB2cyBQb2ludHMgU2NvcmVkCmlucHV0IDwtIG5iYV9yb3N0ZXJbLCBjKCdNUCcsICdQVFMnKV0KcHJpbnQoaGVhZChpbnB1dCkpCgojIEdldCB0aGUgaW5wdXQgdmFsdWVzLgppbnB1dCA8LSBuYmFfcm9zdGVyWywgYygnTVAnLCAnUFRTJyldCgojIFBsb3QgdGhlIGNoYXJ0IGZvciBjYXJzIHdpdGgKIyB3ZWlnaHQgYmV0d2VlbiAxLjUgdG8gNCBhbmQKIyBtaWxlYWdlIGJldHdlZW4gMTAgYW5kIDI1LgpwbG90KHggPSBpbnB1dCRNUCwgeSA9IGlucHV0JFBUUywKCXhsYWIgPSAiTWludXRlcyBQbGF5ZWQiLAoJeWxhYiA9ICJQb2ludHMiLAoJeGxpbSA9IGMoMC4wLCA0OCksCgl5bGltID0gYygwLjAsIDM1KSwJIAoJbWFpbiA9ICJNaW51dGVzIFBsYXllZCB2cyBQb2ludHMgU2NvcmVkIgopCgpgYGAKCgpgYGB7cn0KI0RhdGEgVmlzdWFsaXphdGlvbiBmb3IgRmllbGQgR29hbHMgQXR0ZW1wbGVkIHZzIEZpZWxkIEdvYWxzIE1hZGUKaW5wdXRfMiA8LSBuYmFfcm9zdGVyWywgYygnRkdBJywgJ0ZHJyldCnByaW50KGhlYWQoaW5wdXRfMikpCgojIEdldCB0aGUgaW5wdXQgdmFsdWVzLgppbnB1dF8yIDwtIG5iYV9yb3N0ZXJbLCBjKCdGR0EnLCAnRkcnKV0KCiMgUGxvdCB0aGUgY2hhcnQgZm9yIHBsYXllcnMgd2l0aAojIGZpZWxkIGdvYWwgYXR0ZW1wdHMgYmV0d2VlbiAwLjAgdG8gMjUuMCBhbmQKYl9GRzwtbWF4KGlucHV0XzIkRkcsbmEucm09VCkKYl9GR0E8LW1heChpbnB1dF8yJEZHQSxuYS5ybT1UKQojIEZpZWxkIEdvYWwgTWFkZSBiZXR3ZWVuIDAuMCBhbmQgMjUuMApwbG90KHggPSBpbnB1dF8yJEZHQSwgeSA9IGlucHV0XzIkRkcsCgl4bGFiID0gIkZpZWxkIEdvYWwgQXR0ZW1wdHMiLAoJeWxhYiA9ICJGaWVsZCBHb2FsIE1hZGUiLAoJeGxpbSA9IGMoMC4wLCBiX0ZHQSksCgl5bGltID0gYygwLjAsIGJfRkcpLAkgCgltYWluID0gIkZpZWxkIEdvYWwgQXR0ZW1wdCB2cyBGaWVsZCBHb2FsIE1hZGUiCikKCgpgYGAKCgoKYGBge3J9CiMgQ3JlYXRlIHRoZSBkYXRhIGZvciB0aGUgY2hhcnQKQSA8LSBjKG5iYV9yb3N0ZXIkUFRTKQpCIDwtIGMoIlBGIiwgIlBHIiwgIlNGIiwgIkMiLCAiU0ciKQoKIyBQbG90IHRoZSBiYXIgY2hhcnQKZ2dwbG90KG5iYV9yb3N0ZXIsIGFlcyh4PVBvcywgZmlsbD1QVFMpKSsgIAoKZ2VvbV9iYXIoKSsgIAoKdGhlbWVfY2xhc3NpYygxNikrICAKCnhsYWIoIlBvc2l0aW9uIikrICAKCnlsYWIoIlBvaW50cyIpIAoKYGBgCgoKKipBU1NJR05NRU5UIDE6KiogKklzIHRoZSBkYXRhICJjbGVhbiI/IEFyZSB0aGVyZSBhbnkgbWlzc2luZyB2YWx1ZXMgdG8gYmUgYWNjb3VudGVkIGZvci9hZGRyZXNzZWQ/IElmIHRoZXJlIGFyZSBhbnkgZGF0YSBxdWFsaXR5IGlzc3VlcywqCgogLSAqYS4gcHJvcG9zZSBhIG1ldGhvZCB0byByZXNvbHZlIHRoZW0qCiAgICAgICAKSW5pdGlhbCB0aG91Z2h0IHRvIGNoYW5nZSB0aGUgZGVmYXVsdCBjaGFyYWN0ZXIgdG8gZG91YmxlIGdpdmVuIHRoYXQgd2UgaGF2ZSBmcmFjdGlvbmVkIHZhbHVlcy4gaSB0aGluayB0aGUgY29sdW1ucyBzaG91bGQgYmUgY2hhbmdlZCBmcm9tICIgY2hhcmFyY3RlciIgdG8gImRvdWJsZSIgCgoKIC0gKmIuIGp1c3RpZnkgdGhlIHZhbGlkaXR5IG9mIHlvdXIgYXBwcm9hY2gqCnJlbW92aW5nIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgZGF0YSBmcm9tIHRoZSBkYXRhc2V0LCB1c2luZyB0aGUgZnVuY3Rpb24gIm5hLm9taXQiIHdoaWNoIHdpbGwgcmVtb3ZlIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyBmcm9tIG91ciBkYXRhc2V0CgoKIC0gKmMuIGltcGxlbWVudCB5b3VyIHByb3Bvc2VkIGNoYW5nZXMqCgoKRm9yIHBsYXllcnMgd2hvIGFyZSBtaXNzaW5nIGRhdGEKYGBge3J9Cm5iYV9yb3N0ZXI8LW5hLm9taXQobmJhX3Jvc3RlcikKCm5iYV9yb3N0ZXIKCgoKCiMgQ29udmVydCBzcGVjaWZpYyBjb2x1bW5zIGZyb20gY2hhcmFjdGVyIHRvIGRvdWJsZQojIENvbnZlcnQgYWxsIGNoYXJhY3RlciBjb2x1bW5zIHRvIGRvdWJsZQpuYmFfcm9zdGVyICU+JQogICBtdXRhdGUoYWNyb3NzKEc6UFRTLCBhcy5udW1lcmljKSkKCgoKYGBgCgpUbyBkZXRlcm1pbmUgd2hldGhlciBhIHBsYXllciBpcyAidG9wIHRpZXIiIGFuZCBzaG91bGQgYmUgY29uc2lkZXJlZCBhIHBhcnQgb2YgYSAiQmlnIDMiIGxpbmV1cCwgb3RoZXIgYXV0aG9ycyBoYXZlIHRyYW5zZm9ybWVkIHRyYWRpdGlvbmFsIHN0YXRzIHRvIGNyZWF0ZSBtZXRyaWNzIHN1Y2ggYXMKClBSQSA9IFBPSU5UUyArIFJFQk9VTkRTICsgQVNTSVNUUyAKCldlIHdpbGwgY29uc2lkZXIgYWR2YW5jZWQgc3RhdGlzdGljcyBzdWNoIGFzIFBMQVlFUiBFRkZJRklFTkNZIFJBVElORzoKClBFUiA9IChQVFMgKyBSRUIgKyBBU1QgKyBTVEwgKyBCTEsg4oiSIE1pc3NlZCBGRyDiiJIgTWlzc2VkIEZUIC0gVE8pIC9HUAoKSW4gcGFydGljdWxhciwgVmFsdWUgb3ZlciBSZXBsYWNlbWVudCAoVk9SUCkgc2VlbXMgdG8gZG8gYSBzb2xpZCBqb2Igb2YgaWRlbnRpZnlpbmcgdGhlIGJlc3QgcGxheWVycyBpbiB0aGUgbGVhZ3VlLgoKVGhlIHNjcmlwdCBiZWxvdyBwcm92aWRlIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgYWR2YW5jZWQgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgpgYGB7cn0KCiMgRnVuY3Rpb24gdG8gZ2V0IE5CQSBhZHZhbmNlZCBzdGF0cyBmb3IgYSBzcGVjaWZpZWQgeWVhcgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICAjIENvbnN0cnVjdCB0aGUgVVJMIGZvciB0aGUgc3BlY2lmaWVkIHllYXIKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIFJlYWQgdGhlIEhUTUwgY29udGVudCBmcm9tIHRoZSBVUkwKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgCiAgIyBFeHRyYWN0IHRoZSB0YWJsZSBjb250YWluaW5nIHRoZSBhZHZhbmNlZCBwbGF5ZXIgc3RhdGlzdGljcwogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI2FkdmFuY2VkX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEgKHJlbW92ZSBoZWFkZXIgcm93cyB0aGF0IG1pZ2h0IGJlIGR1cGxpY2F0ZWQpCiAjIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICMgIGZpbHRlcihQbGF5ZXIgIT0gIlBsYXllciIpCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgojIEV4YW1wbGUgdXNhZ2UKeWVhciA8LSAyMDE4ICAjIFNwZWNpZnkgdGhlIHllYXIKbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGdldF9uYmFfYWR2YW5jZWRfc3RhdHMoeWVhcikKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBhZHZhbmNlZCBzdGF0cwpoZWFkKG5iYV9hZHZhbmNlZF9zdGF0cykKCgpgYGAKCioqQVNTSUdOTUVOVCAyOioqICpJcyB0aGUgYWR2YW5jZWQgZGF0YSAiY2xlYW4iPyBBcmUgdGhlcmUgYW55IG1pc3NpbmcgdmFsdWVzIHRvIGJlIGFjY291bnRlZCBmb3IvYWRkcmVzc2VkPyBJZiB0aGVyZSBhcmUgYW55IGRhdGEgcXVhbGl0eSBpc3N1ZXMsKgoKIC0gKmEuIHByb3Bvc2UgYSBtZXRob2QgdG8gcmVzb2x2ZSB0aGVtKgoKIC0gKmIuIGp1c3RpZnkgdGhlIHZhbGlkaXR5IG9mIHlvdXIgYXBwcm9hY2gqCgogLSAqYy4gaW1wbGVtZW50IHlvdXIgcHJvcG9zZWQgY2hhbmdlcyoKIAogY2xlYW5pbmcgc2ltaWxhciB0byBmaXJzdCBvbmUgCiAKIAogVGhlIHNjcmlwdCBiZWxvdyBwcm92aWRlIGNvZGUgdG8gY2xlYW4gb3V0IHRoZSBxdWFsaXR5IGlzc3VlcyBwcmVzZW50ZWQgaW4gdGhlIGRhdGFmcmFtZQogCiAKIApgYGB7cn0KIzEgV2Ugd2FudCB0byBvcmRlciB0aGUgYXRobGV0ZXMgbmFtZSB0byBhbHBoYWJldGljYWwgb3JkZXIgdG8gY2xlYW4gb3V0IHRoZSBmaWxsZXIgaGVhZGVycyBwcmVzZW50CgpuZXdkYXRhZnJhbWU8LSBkYXRhZnJhbWVbb3JkZXIoZGF0YWZyYW1lJFBsYXllcildCgojMiBOb3cgd2Ugd2FudCB0byByZW1vdmUgdGhlIGZpbGxlciByb3dzIHRoYXQgaGFkIGJlZW4gdXNlZCBhcyBoZWFkZXJzIG9uIHRoZSB3ZWJwYWdlCgpuZXdkYXRhLmZyYW1lPC1kYXRhZnJhbWVbLWMoNTAyOjUyNiksIF0KCiMzIG5vdyB3ZSB3YW50IHRvIHJlbW92ZSBhbGwgdGhlIE4vQXMgZnJvbSB0aGUgZGF0YXNldApkYXRhZnJhbWUgJT4lIAogIHNlbGVjdCh3aGVyZSh+IWFsbChpcy5uYSguKSkpKQpgYGAKIAogCiAKYGBge3J9Cgojd2FudCB0byBvcmRlciBieSBhbHBoYWJldGljIG5hbWUgdG8gbWFrZSBjbGVhbmluZyBvdXQgdGhlIGZpbGxlciBoZWFkZXJzIGZyb20gdGhlIGRhdGFzZXQKQU9fbmJhX2FkdmFuY2VkX3N0YXRzIDwtIG5iYV9hZHZhbmNlZF9zdGF0c1tvcmRlcihuYmFfYWR2YW5jZWRfc3RhdHMkUGxheWVyKSxdCgoKCiMgcmVtb3ZlIGZpbGxlciByb3dzIHRoYXQgaGFkIGJlZW4gcHJldmlvdXNseSB1c2VkIGFzIGhlYWRlcnMgb24gd2VicGFnZQpBT19uYmFfYWR2YW5jZWRfc3RhdHM8LSBBT19uYmFfYWR2YW5jZWRfc3RhdHNbLWMoNTAyOjUyNiksIF0KCgojcmVtb3ZlIG5hIGZyb20gZGF0YWZyYW1lCkFPX25iYV9hZHZhbmNlZF9zdGF0cyAlPiUgCiAgc2VsZWN0KHdoZXJlKH4hYWxsKGlzLm5hKC4pKSkpCiNyZW1vdmluZyBjb2x1bW4gMjAgYW5kIDI1IGZyb20gZGF0YWZyYW1lIHNpbmNlIHRoZXlyZSBibGFua3MKQU9fbmJhX2FkdmFuY2VkX3N0YXRzPC1BT19uYmFfYWR2YW5jZWRfc3RhdHNbLC0yMF0KQU9fbmJhX2FkdmFuY2VkX3N0YXRzPC1BT19uYmFfYWR2YW5jZWRfc3RhdHNbLC0yNF0KCgojY2hhbmdlIHJhbmdlIG9mIGNsb3VtbnMgPGRibD4gZnJvbSA8Y2hyPgoKQU9fbmJhX2FkdmFuY2VkX3N0YXRzICU+JQogICBtdXRhdGUoYWNyb3NzKEc6Vk9SUCwgYXMubnVtZXJpYykpCgoKCgpgYGAKCgoqKkFTU0lHTk1FTlQgMzoqKiAqTWVyZ2UgdGhlIGNsZWFuZWQgdXAgZGF0YXNldHMgdG8gY3JlYXRlIG9uZSBuZXcgZGF0YSBmcmFtZSB3aXRoIHRoZSB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgc3RhdHMuKgoKCgpgYGB7cn0KI2hvdyB0byBtZXJnZSB0d28gZmlsZXMgaW50byBvbmUgbmV3IGRhdGEgZnJhbWUKbmJhX21lcmdlPC1tZXJnZShuYmFfcm9zdGVyLCBBT19uYmFfYWR2YW5jZWRfc3RhdHMsIGJ5LnggPSBjKCJSayIsICJQbGF5ZXIiLCAiUG9zIiwiQWdlIiwgIlRtIiwiRyIpLCBieS55ID0gYygiUmsiLCAiUGxheWVyIiwgIlBvcyIsIkFnZSIsICJUbSIsIkciKSAsIGFsbC54ID0gVFJVRSwgYWxsLnkgPSBUUlVFKQoKCmhlYWQobmJhX21lcmdlKQpgYGAKCgoqKkFTU0lHTk1FTlQgNDoqKiAqTWFrZSBhIGZ1bmN0aW9uIHdpdGggYXJndW1lbnQgYHllYXJgIHRoYXQgb3V0cHV0cyBvbmUgZGF0YWZyYW1lIHdpdGggdGhlIG1lcmdlZCB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgZGF0YS4qIAoKYGBge3J9Cgpjb21iaW5lZF9uYmFfc3RhdHM8LWZ1bmN0aW9uKHllYXIpewpnZXRfbmJhX3Jvc3RlcjIgPC0gZnVuY3Rpb24oeWVhcikgewogICMgQ29uc3RydWN0IHRoZSBVUkwgZm9yIHRoZSBzcGVjaWZpZWQgeWVhcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3Blcl9nYW1lLmh0bWwiKQogIAogICMgUmVhZCB0aGUgSFRNTCBjb250ZW50IGZyb20gdGhlIFVSTAogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKCgogICMgRXh0cmFjdCB0aGUgdGFibGUgY29udGFpbmluZyB0aGUgcGxheWVyIHN0YXRpc3RpY3MKICByb3N0ZXJfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjcGVyX2dhbWVfc3RhdHMiKSAlPiUKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YSAocmVtb3ZlIGhlYWRlciByb3dzIHRoYXQgbWlnaHQgYmUgZHVwbGljYXRlZCkKICByb3N0ZXJfdGFibGUgPC0gcm9zdGVyX3RhYmxlICU+JQogICAgZmlsdGVyKFBsYXllciAhPSAiUGxheWVyIikKICAgIHJldHVybihyb3N0ZXJfdGFibGUpCn0KICAKICB5ZWFyIDwtIDIwMjMgICMgU3BlY2lmeSB0aGUgeWVhcgpuYmFfcm9zdGVyMiA8LSBnZXRfbmJhX3Jvc3RlcjIoeWVhcikKCiNQcmludCB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIHJvc3RlcgpoZWFkKG5iYV9yb3N0ZXIpCnRhaWwobmJhX3Jvc3RlcikKCgojdGFrZSBvdXQgdGhlIE4vQSAKbmJhX3Jvc3RlcjI8LW5hLm9taXQobmJhX3Jvc3RlcjIpCgoKIyBDb252ZXJ0IHNwZWNpZmljIGNvbHVtbnMgZnJvbSBjaGFyYWN0ZXIgdG8gZG91YmxlCgpuYmFfcm9zdGVyMiAlPiUKICAgbXV0YXRlKGFjcm9zcyhHOlBUUywgYXMubnVtZXJpYykpCgojQURWQU5DRUQgU1RBVFMKCiMgRnVuY3Rpb24gdG8gZ2V0IE5CQSBhZHZhbmNlZCBzdGF0cyBmb3IgYSBzcGVjaWZpZWQgeWVhcgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICAjIENvbnN0cnVjdCB0aGUgVVJMIGZvciB0aGUgc3BlY2lmaWVkIHllYXIKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIFJlYWQgdGhlIEhUTUwgY29udGVudCBmcm9tIHRoZSBVUkwKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgCiAgIyBFeHRyYWN0IHRoZSB0YWJsZSBjb250YWluaW5nIHRoZSBhZHZhbmNlZCBwbGF5ZXIgc3RhdGlzdGljcwogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI2FkdmFuY2VkX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEgKHJlbW92ZSBoZWFkZXIgcm93cyB0aGF0IG1pZ2h0IGJlIGR1cGxpY2F0ZWQpCiAjIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICMgIGZpbHRlcihQbGF5ZXIgIT0gIlBsYXllciIpCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgojIEV4YW1wbGUgdXNhZ2UKeWVhciA8LSAyMDIzICAjIFNwZWNpZnkgdGhlIHllYXIKbmJhX2FkdmFuY2VkX3N0YXRzMjwtIGdldF9uYmFfYWR2YW5jZWRfc3RhdHMoeWVhcikKCiMgUHJpbnQgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBhZHZhbmNlZCBzdGF0cwpoZWFkKG5iYV9hZHZhbmNlZF9zdGF0czIpCgoKCiN3YW50IHRvIG9yZGVyIGJ5IGFscGhhYmV0aWMgbmFtZSB0byBtYWtlIGNsZWFuaW5nIG91dCB0aGUgZmlsbGVyIGhlYWRlcnMgZnJvbSB0aGUgZGF0YXNldApBT19uYmFfYWR2YW5jZWRfc3RhdHMyPC0gbmJhX2FkdmFuY2VkX3N0YXRzMltvcmRlcihuYmFfYWR2YW5jZWRfc3RhdHMyJFBsYXllciksXQoKCgoKCiNyZW1vdmUgbmEgZnJvbSBkYXRhZnJhbWUKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiAlPiUgCiAgc2VsZWN0KHdoZXJlKH4hYWxsKGlzLm5hKC4pKSkpCiNyZW1vdmluZyBjb2x1bW4gMjAgYW5kIDI1IGZyb20gZGF0YWZyYW1lIHNpbmNlIHRoZXlyZSBibGFua3MKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMjwtQU9fbmJhX2FkdmFuY2VkX3N0YXRzMlssLTIwXQpBT19uYmFfYWR2YW5jZWRfc3RhdHMyPC1BT19uYmFfYWR2YW5jZWRfc3RhdHMyWywtMjRdCgoKIyByZW1vdmUgZmlsbGVyIHJvd3MgdGhhdCBoYWQgYmVlbiBwcmV2aW91c2x5IHVzZWQgYXMgaGVhZGVycyBvbiB3ZWJwYWdlCgoKQU9fbmJhX2FkdmFuY2VkX3N0YXRzMiA8LSBBT19uYmFfYWR2YW5jZWRfc3RhdHMyW0FPX25iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyICE9ICJQbGF5ZXIiLF0KCkFPX25iYV9hZHZhbmNlZF9zdGF0czIkUGxheWVyIDwtIGZhY3RvcihBT19uYmFfYWR2YW5jZWRfc3RhdHMyJFBsYXllcikKCgoKCiNjaGFuZ2UgcmFuZ2Ugb2YgY2xvdW1ucyA8ZGJsPiBmcm9tIDxjaHI+CgpBT19uYmFfYWR2YW5jZWRfc3RhdHMyICU+JQogICBtdXRhdGUoYWNyb3NzKEc6Vk9SUCwgYXMubnVtZXJpYykpCiAgIAogICBuYmFfbWVyZ2U8LW1lcmdlKG5iYV9yb3N0ZXIyLCBBT19uYmFfYWR2YW5jZWRfc3RhdHMyLCBieS54ID0gYygiUmsiLCAiUGxheWVyIiwgIlBvcyIsIkFnZSIsICJUbSIsIkciKSwgYnkueSA9IGMoIlJrIiwgIlBsYXllciIsICJQb3MiLCJBZ2UiLCAiVG0iLCJHIikgLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gVFJVRSkKICAgCn0KCgoKYGBgCgoKKipBU1NJR05NRU5UIDU6KiogKk1ha2UgdGhpcyBmaWxlIG1vcmUgdmlzdWFsbHkgYXBwZWFsbmcsIHdpdGggaGVhZGVycywgYnVsbGV0IHBvaW50cywgc2VjdGlvbnMgYW5kIHN1YnNlY3Rpb25zIGFzIHlvdSBzZWUgZml0LiBZb3UgbWF5IGNvbnNpZGVyIG1pZ3JhdGluZyBvdmVyIHRvIFF1YXJ0byBmb3IgdGhpcyByZWFzb24uKgoKCg==
=======
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhlIGdvYWwgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGludmVzdGlnYXRlIGhvdyBwYXJ0bmVyc2hpcHMgaW52b2x2aW5nIG11bHRpcGxlIHRvcC10aWVyIHBsYXllcnMgaW4gdGhlIE5CQSBpbXBhY3RzIHZhcmlvdXMgcGVyZm9ybWFuY2UgbWVhc3VyZXMgYW5kIHRlYW0gb3V0Y29tZXMuIEFtb25nIHRoZSByZXNlYXJjaCBxdWVzdGlvbnMgd2Ugd291bGQgbGlrZSB0byBleHBsb3JlIGFyZSB0aGUgZm9sbG93aW5nOgoKKlsgSU5TRVJUIFJFU0VBUkNIIFFVRVNUSU9OUyBIRVJFIF0qCgpUbyBiZSBhYmxlIHRvIGludmVzdGlnYXRlLCB3ZSBuZWVkIHRvIHB1bGwgZGF0YSBmcm9tIG11bHRpcGxlIE5CQSBzZWFzb25zLiBUaGUgc2NyaXB0IGJlbG93IHByb3ZpZGVzIGNvZGUgdG8gY3JlYXRlIGZ1bmN0aW9ucyB0aGF0IHB1bGwgdHJhZGl0aW9uYWwgc3RhdHMgZm9yIGV2ZXJ5IHBsYXllciBmb3IgYSBnaXZlbiB1c2VyLWRlZmluZWQgc2Vhc29uLgoKTG9hZCBuZWNlc3NhcnkgbGlicmFyaWVzCmBgYHtyfQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShodHRyKQoKYGBgCgpGdW5jdGlvbiB0byBnZXQgTkJBIHJvc3RlciBmb3IgYSBzcGVjaWZpZWQgeWVhcgoKCkV4YW1wbGUgdXNhZ2UKCgoKCgoqKkFTU0lHTk1FTlQgMToqKiAqSXMgdGhlIGRhdGEgImNsZWFuIj8gQXJlIHRoZXJlIGFueSBtaXNzaW5nIHZhbHVlcyB0byBiZSBhY2NvdW50ZWQgZm9yL2FkZHJlc3NlZD8gSWYgdGhlcmUgYXJlIGFueSBkYXRhIHF1YWxpdHkgaXNzdWVzLCoKCiAtICphLiBwcm9wb3NlIGEgbWV0aG9kIHRvIHJlc29sdmUgdGhlbSoKICAgICAgIApJbml0aWFsIHRob3VnaHQgdG8gY2hhbmdlIHRoZSBkZWZhdWx0IGNoYXJhY3RlciB0byBkb3VibGUgZ2l2ZW4gdGhhdCB3ZSBoYXZlIGZyYWN0aW9uZWQgdmFsdWVzLiBpIHRoaW5rIHRoZSBjb2x1bW5zIHNob3VsZCBiZSBjaGFuZ2VkIGZyb20gIiBjaGFyYXJjdGVyIiB0byAiZG91YmxlIiAKCgogLSAqYi4ganVzdGlmeSB0aGUgdmFsaWRpdHkgb2YgeW91ciBhcHByb2FjaCoKcmVtb3Zpbmcgb2JzZXJ2YXRpb25zIHdpdGggbWlzc2luZyBkYXRhIGZyb20gdGhlIGRhdGFzZXQsIHVzaW5nIHRoZSBmdW5jdGlvbiAibmEub21pdCIgd2hpY2ggd2lsbCByZW1vdmUgcm93cyB3aXRoIG1pc3NpbmcgdmFsdWVzIGZyb20gb3VyIGRhdGFzZXQKCgogLSAqYy4gaW1wbGVtZW50IHlvdXIgcHJvcG9zZWQgY2hhbmdlcyoKCgoqKkFTU0lHTk1FTlQgMjoqKiAqSXMgdGhlIGFkdmFuY2VkIGRhdGEgImNsZWFuIj8gQXJlIHRoZXJlIGFueSBtaXNzaW5nIHZhbHVlcyB0byBiZSBhY2NvdW50ZWQgZm9yL2FkZHJlc3NlZD8gSWYgdGhlcmUgYXJlIGFueSBkYXRhIHF1YWxpdHkgaXNzdWVzLCoKCiAtICphLiBwcm9wb3NlIGEgbWV0aG9kIHRvIHJlc29sdmUgdGhlbSoKCiAtICpiLiBqdXN0aWZ5IHRoZSB2YWxpZGl0eSBvZiB5b3VyIGFwcHJvYWNoKgoKIC0gKmMuIGltcGxlbWVudCB5b3VyIHByb3Bvc2VkIGNoYW5nZXMqCiAKIGNsZWFuaW5nIHNpbWlsYXIgdG8gZmlyc3Qgb25lIAogCiAKIFRoZSBzY3JpcHQgYmVsb3cgcHJvdmlkZSBjb2RlIHRvIGNsZWFuIG91dCB0aGUgcXVhbGl0eSBpc3N1ZXMgcHJlc2VudGVkIGluIHRoZSBkYXRhZnJhbWUKIAogCiAKYGBge3J9CgpgYGAKIAogCiAKYGBge3J9CgpgYGAKCgoqKkFTU0lHTk1FTlQgMzoqKiAqTWVyZ2UgdGhlIGNsZWFuZWQgdXAgZGF0YXNldHMgdG8gY3JlYXRlIG9uZSBuZXcgZGF0YSBmcmFtZSB3aXRoIHRoZSB0cmFkaXRpb25hbCBhbmQgYWR2YW5jZWQgc3RhdHMuKgoKCgpgYGB7cn0KI0dldCBOQkEgVG90YWxzIFN0YXRpc3RpY3MKZ2V0X25iYV90b3RhbHNfc3RhdHMgPC0gZnVuY3Rpb24oeWVhcikgewogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3LmJhc2tldGJhbGwtcmVmZXJlbmNlLmNvbS9sZWFndWVzL05CQV8iLCB5ZWFyLCAiX3RvdGFscy5odG1sIikKICB3ZWJwYWdlIDwtIHJlYWRfaHRtbCh1cmwpCiAgdG90YWxzX3N0YXRzX3RhYmxlIDwtIHdlYnBhZ2UgJT4lCiAgICBodG1sX25vZGUoInRhYmxlI3RvdGFsc19zdGF0cyIpICU+JQogICAgaHRtbF90YWJsZShmaWxsID0gVFJVRSkKCiAgIyBDbGVhbiB1cCBjb2x1bW4gbmFtZXMKICBjb2xuYW1lcyh0b3RhbHNfc3RhdHNfdGFibGUpIDwtIG1ha2UubmFtZXMoY29sbmFtZXModG90YWxzX3N0YXRzX3RhYmxlKSwgdW5pcXVlID0gVFJVRSkKCiAgIyBDbGVhbiB0aGUgZGF0YQogIHRvdGFsc19zdGF0c190YWJsZSA8LSB0b3RhbHNfc3RhdHNfdGFibGUgJT4lCiAgICBmaWx0ZXIoIWlzLm5hKFBsYXllcikgJiBQbGF5ZXIgIT0gIlBsYXllciIpICAjIEVuc3VyZSBubyBOQSBvciBkdXBsaWNhdGUgaGVhZGVyIHJvd3MKICAKICByZXR1cm4odG90YWxzX3N0YXRzX3RhYmxlKQp9CgoKZ2V0X25iYV9hZHZhbmNlZF9zdGF0cyA8LSBmdW5jdGlvbih5ZWFyKSB7CiAgdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly93d3cuYmFza2V0YmFsbC1yZWZlcmVuY2UuY29tL2xlYWd1ZXMvTkJBXyIsIHllYXIsICJfYWR2YW5jZWQuaHRtbCIpCiAgCiAgIyBGZXRjaCB3ZWJwYWdlCiAgd2VicGFnZSA8LSB0cnlDYXRjaCh7CiAgICByZWFkX2h0bWwoR0VUKHVybCwgdXNlcl9hZ2VudCgiTW96aWxsYS81LjAiKSkpCiAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7CiAgICBzdG9wKCJFcnJvciBmZXRjaGluZyB3ZWJwYWdlOiAiLCBlJG1lc3NhZ2UpCiAgfSkKICAKICAjIEV4dHJhY3QgdGFibGUgd2l0aCB1cGRhdGVkIElECiAgYWR2YW5jZWRfc3RhdHNfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjYWR2YW5jZWQiKSAlPiUgICMgVXBkYXRlZCBzZWxlY3RvciB0byBtYXRjaCB0aGUgbmV3IElECiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICMgQ2xlYW4gdXAgY29sdW1uIG5hbWVzCiAgY29sbmFtZXMoYWR2YW5jZWRfc3RhdHNfdGFibGUpIDwtIG1ha2UubmFtZXMoY29sbmFtZXMoYWR2YW5jZWRfc3RhdHNfdGFibGUpLCB1bmlxdWUgPSBUUlVFKQogIAogICMgQ2xlYW4gdGhlIGRhdGEKICBhZHZhbmNlZF9zdGF0c190YWJsZSA8LSBhZHZhbmNlZF9zdGF0c190YWJsZSAlPiUKICAgIGZpbHRlcighaXMubmEoUGxheWVyKSAmIFBsYXllciAhPSAiUGxheWVyIikgICMgUmVtb3ZlIE5BIHJvd3MgYW5kIGR1cGxpY2F0ZSBoZWFkZXJzCiAgCiAgcmV0dXJuKGFkdmFuY2VkX3N0YXRzX3RhYmxlKQp9CgoKYGBgCgoKKipBU1NJR05NRU5UIDQ6KiogKk1ha2UgYSBmdW5jdGlvbiB3aXRoIGFyZ3VtZW50IGB5ZWFyYCB0aGF0IG91dHB1dHMgb25lIGRhdGFmcmFtZSB3aXRoIHRoZSBtZXJnZWQgdHJhZGl0aW9uYWwgYW5kIGFkdmFuY2VkIGRhdGEuKiAKCgpPZmZpY2lhbCBDbGVhbmluZyBGdW5jdGlvbiB0aGF0IHdvcmtzIGFzIG9mIDEwLzI5LzIwMjQKYGBge3J9CgojR2V0IE5CQSBUb3RhbHMgU3RhdGlzdGljcwpnZXRfbmJhX3RvdGFsc19zdGF0cyA8LSBmdW5jdGlvbih5ZWFyKSB7CiAgdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly93d3cuYmFza2V0YmFsbC1yZWZlcmVuY2UuY29tL2xlYWd1ZXMvTkJBXyIsIHllYXIsICJfdG90YWxzLmh0bWwiKQogIHdlYnBhZ2UgPC0gcmVhZF9odG1sKHVybCkKICB0b3RhbHNfc3RhdHNfdGFibGUgPC0gd2VicGFnZSAlPiUKICAgIGh0bWxfbm9kZSgidGFibGUjdG90YWxzX3N0YXRzIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQoKICAjIENsZWFuIHVwIGNvbHVtbiBuYW1lcwogIGNvbG5hbWVzKHRvdGFsc19zdGF0c190YWJsZSkgPC0gbWFrZS5uYW1lcyhjb2xuYW1lcyh0b3RhbHNfc3RhdHNfdGFibGUpLCB1bmlxdWUgPSBUUlVFKQoKICAjIENsZWFuIHRoZSBkYXRhCiAgdG90YWxzX3N0YXRzX3RhYmxlIDwtIHRvdGFsc19zdGF0c190YWJsZSAlPiUKICAgIGZpbHRlcighaXMubmEoUGxheWVyKSAmIFBsYXllciAhPSAiUGxheWVyIikgICMgRW5zdXJlIG5vIE5BIG9yIGR1cGxpY2F0ZSBoZWFkZXIgcm93cwogIAogIHJldHVybih0b3RhbHNfc3RhdHNfdGFibGUpCn0KCgpnZXRfbmJhX2FkdmFuY2VkX3N0YXRzIDwtIGZ1bmN0aW9uKHllYXIpIHsKICB1cmwgPC0gcGFzdGUwKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vbGVhZ3Vlcy9OQkFfIiwgeWVhciwgIl9hZHZhbmNlZC5odG1sIikKICAKICAjIEZldGNoIHdlYnBhZ2UKICB3ZWJwYWdlIDwtIHRyeUNhdGNoKHsKICAgIHJlYWRfaHRtbChHRVQodXJsLCB1c2VyX2FnZW50KCJNb3ppbGxhLzUuMCIpKSkKICB9LCBlcnJvciA9IGZ1bmN0aW9uKGUpIHsKICAgIHN0b3AoIkVycm9yIGZldGNoaW5nIHdlYnBhZ2U6ICIsIGUkbWVzc2FnZSkKICB9KQogIAogICMgRXh0cmFjdCB0YWJsZSB3aXRoIHVwZGF0ZWQgSUQKICBhZHZhbmNlZF9zdGF0c190YWJsZSA8LSB3ZWJwYWdlICU+JQogICAgaHRtbF9ub2RlKCJ0YWJsZSNhZHZhbmNlZCIpICU+JSAgIyBVcGRhdGVkIHNlbGVjdG9yIHRvIG1hdGNoIHRoZSBuZXcgSUQKICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpCiAgCiAgIyBDbGVhbiB1cCBjb2x1bW4gbmFtZXMKICBjb2xuYW1lcyhhZHZhbmNlZF9zdGF0c190YWJsZSkgPC0gbWFrZS5uYW1lcyhjb2xuYW1lcyhhZHZhbmNlZF9zdGF0c190YWJsZSksIHVuaXF1ZSA9IFRSVUUpCiAgCiAgIyBDbGVhbiB0aGUgZGF0YQogIGFkdmFuY2VkX3N0YXRzX3RhYmxlIDwtIGFkdmFuY2VkX3N0YXRzX3RhYmxlICU+JQogICAgZmlsdGVyKCFpcy5uYShQbGF5ZXIpICYgUGxheWVyICE9ICJQbGF5ZXIiKSAgIyBSZW1vdmUgTkEgcm93cyBhbmQgZHVwbGljYXRlIGhlYWRlcnMKICAKICByZXR1cm4oYWR2YW5jZWRfc3RhdHNfdGFibGUpCn0KCmdldF9jbGVhbmVkX25iYV9zdGF0cyA8LSBmdW5jdGlvbih5ZWFyKSB7CiAgIyBGZXRjaCB0b3RhbHMgYW5kIGFkdmFuY2VkIHN0YXRzCiAgbmJhX3RvdGFscyA8LSBnZXRfbmJhX3RvdGFsc19zdGF0cyh5ZWFyKQogIG5iYV9hZHZhbmNlZCA8LSBnZXRfbmJhX2FkdmFuY2VkX3N0YXRzKHllYXIpCgogICMgUHJpbnQgdG8gY2hlY2sgZGF0YXNldHMgKE9wdGlvbmFsKQogIHByaW50KCJOQkEgVG90YWxzOiIpCiAgcHJpbnQoaGVhZChuYmFfdG90YWxzKSkKICBwcmludCgiTkJBIEFkdmFuY2VkOiIpCiAgcHJpbnQoaGVhZChuYmFfYWR2YW5jZWQpKQoKICAjIENsZWFuIFBsYXllciBuYW1lcyBpbiB0aGUgYWR2YW5jZWQgZGF0YXNldDogcmVtb3ZlIHRoZSBhc3RlcmlzayBhbmQgdHJpbSBzcGFjZXMKICBuYmFfYWR2YW5jZWQgPC0gbmJhX2FkdmFuY2VkICU+JQogICAgbXV0YXRlKFBsYXllciA9IHRyaW13cyhnc3ViKCJcXCoiLCAiIiwgUGxheWVyKSkpICAjIFJlbW92ZSBhc3RlcmlzawoKICAjIEVuc3VyZSB0aGF0IHRoZSBhZHZhbmNlZCBzdGF0cyBjb25zaWRlciB0aGUgY2xlYW5lZCBwbGF5ZXIgbmFtZXMKICBuYmFfYWR2YW5jZWQgPC0gbmJhX2FkdmFuY2VkICU+JQogICAgbXV0YXRlKFBsYXllciA9IHRyaW13cyhQbGF5ZXIpKQoKICAjIE1lcmdlIHRoZSBkYXRhc2V0cyBvbiBQbGF5ZXIsIFBvcywgRywgYW5kIE1QCiAgbmJhX21lcmdlIDwtIG1lcmdlKG5iYV90b3RhbHMsIG5iYV9hZHZhbmNlZCwgCiAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiUGxheWVyIiwgIlBvcyIsICJHIiwgIk1QIiksIAogICAgICAgICAgICAgICAgICAgICBhbGwueCA9IFRSVUUpCgogICMgRGVidWc6IFNob3cgbWVyZ2VkIGRhdGEgc2FtcGxlCiAgcHJpbnQoIk1lcmdlZCBEYXRhIFNhbXBsZToiKQogIHByaW50KGhlYWQobmJhX21lcmdlKSkKICAKICAjIENoZWNrIGNvbHVtbiBuYW1lcyB0byBjb25maXJtICdUZWFtJyBleGlzdHMKICBwcmludCgiQ29sdW1uIE5hbWVzIEJlZm9yZSBSZW5hbWluZzoiKQogIHByaW50KGNvbG5hbWVzKG5iYV9tZXJnZSkpCgogICMgQ2xlYW4gJ1RlYW0nIGNvbHVtbjogcmVuYW1lICdUbScgdG8gJ1RlYW0nIGlmIHByZXNlbnQKICBpZiAoIlRtIiAlaW4lIGNvbG5hbWVzKG5iYV9tZXJnZSkpIHsKICAgIG5iYV9tZXJnZSA8LSBuYmFfbWVyZ2UgJT4lCiAgICAgIHJlbmFtZShUZWFtID0gVG0pCiAgfQoKICAjIERlYnVnOiBTaG93IGNvbHVtbiBuYW1lcyBhZnRlciByZW5hbWluZwogIHByaW50KCJDb2x1bW4gTmFtZXMgQWZ0ZXIgUmVuYW1pbmcgJ1RtJyB0byAnVGVhbSc6IikKICBwcmludChjb2xuYW1lcyhuYmFfbWVyZ2UpKQoKICAjIEhhbmRsZSBkdXBsaWNhdGUgY29sdW1ucyBsaWtlIFRlYW0ueCwgVGVhbS55LCBBd2FyZHMueCwgQXdhcmRzLnkKICBkdXBsaWNhdGVfY29sdW1ucyA8LSBjb2xuYW1lcyhuYmFfbWVyZ2UpW2dyZXBsKCJcXC54JCIsIGNvbG5hbWVzKG5iYV9tZXJnZSkpXQoKICBmb3IgKGNvbCBpbiBkdXBsaWNhdGVfY29sdW1ucykgewogICAgIyBFeHRyYWN0IHRoZSBiYXNlIG5hbWUgb2YgdGhlIGNvbHVtbiAoZS5nLiwgIlRlYW0iIGZyb20gIlRlYW0ueCIpCiAgICBiYXNlX2NvbCA8LSBzdWIoIlxcLngkIiwgIiIsIGNvbCkKICAgIAogICAgIyBNZXJnZSB0aGUgLnggYW5kIC55IGNvbHVtbnMgaW50byBvbmUKICAgIGlmIChwYXN0ZTAoYmFzZV9jb2wsICIueSIpICVpbiUgY29sbmFtZXMobmJhX21lcmdlKSkgewogICAgICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgICAgIG11dGF0ZSghIWJhc2VfY29sIDo9IGNvYWxlc2NlKGdldChjb2wpLCBnZXQocGFzdGUwKGJhc2VfY29sLCAiLnkiKSkpKSAlPiUKICAgICAgICBzZWxlY3QoLWFsbF9vZihjKGNvbCwgcGFzdGUwKGJhc2VfY29sLCAiLnkiKSkpKSAgIyBEcm9wIHRoZSBvbGQgY29sdW1ucwogICAgfQogIH0KCiAgIyBSZW1vdmUgcGxheWVycyB3aG9zZSB0ZWFtIGlzICJUT1QiLCAiMlRtIiwgb3IgIjNUbSIKICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgZmlsdGVyKCFncmVwbCgiXihUT1R8MlRNfDNUTSkkIiwgVGVhbSkpCgogICMgUmVtb3ZlIHBsYXllcnMgd2l0aCBtdWx0aXBsZSBwb3NpdGlvbnMKICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgZmlsdGVyKCFncmVwbCgiLSIsIFBvcykpCgogICMgUmVtb3ZlIHRoZSAnWCcsICdYLjEnIGNvbHVtbnMgaWYgdGhleSBleGlzdAogIGNvbHVtbnNfdG9fcmVtb3ZlIDwtIGMoIlgiLCAiWC4xIikKICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgc2VsZWN0KC1hbnlfb2YoY29sdW1uc190b19yZW1vdmUpKSAgIyBSZW1vdmUgc3BlY2lmaWVkIGNvbHVtbnMgaWYgdGhleSBleGlzdAoKICAjIE1lcmdlICdSay54JyBhbmQgJ1JrLnknIGNvbHVtbnMKICBpZiAoIlJrLngiICVpbiUgbmFtZXMobmJhX21lcmdlKSAmICJSay55IiAlaW4lIG5hbWVzKG5iYV9tZXJnZSkpIHsKICAgIG5iYV9tZXJnZSA8LSBuYmFfbWVyZ2UgJT4lCiAgICAgIG11dGF0ZShSayA9IGNvYWxlc2NlKGFzLmNoYXJhY3RlcihSay54KSwgYXMuY2hhcmFjdGVyKFJrLnkpKSkgJT4lCiAgICAgIHNlbGVjdCgtUmsueCwgLVJrLnkpCiAgfQoKICAjIE1lcmdlICdBZ2UueCcgYW5kICdBZ2UueScgY29sdW1ucwogIGlmICgiQWdlLngiICVpbiUgbmFtZXMobmJhX21lcmdlKSAmICJBZ2UueSIgJWluJSBuYW1lcyhuYmFfbWVyZ2UpKSB7CiAgICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgICBtdXRhdGUoQWdlID0gY29hbGVzY2UoYXMubnVtZXJpYyhBZ2UueCksIGFzLm51bWVyaWMoQWdlLnkpKSkgJT4lCiAgICAgIHNlbGVjdCgtYyhBZ2UueCwgQWdlLnkpKQogIH0KCiAgIyBSZW9yZGVyIGNvbHVtbnMgZm9yIGNsYXJpdHkKICBjb2x1bW5fb3JkZXIgPC0gYygiUGxheWVyIiwgIlBvcyIsICJBZ2UiLCAiUmsiLCAiRyIsICJNUCIsICJUZWFtIikKICBuYmFfbWVyZ2UgPC0gbmJhX21lcmdlICU+JQogICAgc2VsZWN0KGFsbF9vZihjb2x1bW5fb3JkZXIpLCBldmVyeXRoaW5nKCkpCgogICMgUmV0dXJuIHRoZSBjbGVhbmVkIGFuZCBtZXJnZWQgZGF0YXNldAogIHJldHVybihuYmFfbWVyZ2UpCn0KCiMgRXhhbXBsZSB1c2FnZQpuYmFfZGF0YV8yMDEzIDwtIGdldF9jbGVhbmVkX25iYV9zdGF0cygyMDEzKQoKIyBWaWV3IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgY2xlYW5lZCBkYXRhc2V0CmhlYWQobmJhX2RhdGFfMjAxMykKCgpgYGAKCgpgYGB7cn0KbmJhX2RhdGFfMjAyMiA8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDIyKQpuYmFfZGF0YV8yMDEwIDwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMTApCm5iYV9kYXRhXzIwMTUgPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAxNSkKbmJhX2RhdGFfMjAxMTwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMTEpCm5iYV9kYXRhXzIwMTI8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDEyKQpuYmFfZGF0YV8yMDA5PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAwOSkKbmJhX2RhdGFfMjAwODwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMDgpCm5iYV9kYXRhXzIwMDc8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDA3KQpuYmFfZGF0YV8yMDA2PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAwNikKbmJhX2RhdGFfMjAwNTwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMDUpCm5iYV9kYXRhXzIwMDQ8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDA0KQpuYmFfZGF0YV8yMDAzPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAwMykKbmJhX2RhdGFfMjAwMjwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDIwMDIpCm5iYV9kYXRhXzIwMDE8LWdldF9jbGVhbmVkX25iYV9zdGF0cygyMDAxKQpuYmFfZGF0YV8yMDAwPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMjAwMCkKCgoKbmJhX2RhdGFfMTk5OTwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5OTkpCm5iYV9kYXRhXzE5OTg8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTk4KQpuYmFfZGF0YV8xOTk3PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk5NykKbmJhX2RhdGFfMTk5NjwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5OTYpCm5iYV9kYXRhXzE5OTU8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTk1KQpuYmFfZGF0YV8xOTk0PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk5NCkKbmJhX2RhdGFfMTk5MzwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5OTMpCm5iYV9kYXRhXzE5OTI8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTkyKQpuYmFfZGF0YV8xOTkxPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk5MSkKbmJhX2RhdGFfMTk5MDwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5OTApCm5iYV9kYXRhXzE5ODk8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTg5KQpuYmFfZGF0YV8xOTg4PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk4OCkKbmJhX2RhdGFfMTk4NzwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5ODcpCm5iYV9kYXRhXzE5ODY8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTg2KQpuYmFfZGF0YV8xOTg1PC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk4NSkKbmJhX2RhdGFfMTk4NDwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5ODQpCm5iYV9kYXRhXzE5ODM8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTgzKQpuYmFfZGF0YV8xOTgyPC1nZXRfY2xlYW5lZF9uYmFfc3RhdHMoMTk4MikKbmJhX2RhdGFfMTk4MTwtZ2V0X2NsZWFuZWRfbmJhX3N0YXRzKDE5ODEpCm5iYV9kYXRhXzE5ODA8LWdldF9jbGVhbmVkX25iYV9zdGF0cygxOTgwKQoKcmVhZExpbmVzKCJodHRwczovL3d3dy5iYXNrZXRiYWxsLXJlZmVyZW5jZS5jb20vdGVhbXMvTEFMLzIwMjMuaHRtbCIsIG4gPSAxKQoKYGBgCgoKKipBU1NJR05NRU5UIDU6KiogKk1ha2UgdGhpcyBmaWxlIG1vcmUgdmlzdWFsbHkgYXBwZWFsbmcsIHdpdGggaGVhZGVycywgYnVsbGV0IHBvaW50cywgc2VjdGlvbnMgYW5kIHN1YnNlY3Rpb25zIGFzIHlvdSBzZWUgZml0LiBZb3UgbWF5IGNvbnNpZGVyIG1pZ3JhdGluZyBvdmVyIHRvIFF1YXJ0byBmb3IgdGhpcyByZWFzb24uKgoKCkZpbGUgbG9jYXRvcgpgYGB7cn0KIyBTYXZlIHlvdXIgZGF0YWZyYW1lIGFzIGEgQ1NWIGZpbGUKd3JpdGUuY3N2KG5iYV9yb3N0ZXIyLCBmaWxlID0gImdlbmVyYWxzdGF0cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KEFPX25iYV9hZHZhbmNlZF9zdGF0czIsIGZpbGUgPSAiYWR2YW5jZWRzdGF0cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KG5iYV9kYXRhXzIwMjMsIGZpbGUgPSAibmJhMjAyMy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KG5iYV9kYXRhXzIwMTMsIGZpbGUgPSAibmJhMjAxMy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKZ2V0d2QoKQoKYGBgCgoKCgoK
>>>>>>> Stashed changes